#ifndef _td_object_h_
#define _td_object_h_
#include "chipmunk_object.h"
#include <set>
#include "totem_obj_config.h"
#include "animation.h"

typedef enum {
	kTotemObjNormal = 0,
	kTotemObjFallen,
	kTotemObjBurn,
	kTotemObjPass,
	kTotemObjOver
}t_totem_obj_status;

namespace totem_destroyer{

	class SpaceMgr {
	public:
		typedef std::set<cpShape *>  shapes_map_t;
		SpaceMgr() : m_space(NULL) {initialize();}
		~SpaceMgr() {release();}
		void initialize();
		void release();

		template <typename T>
		T *create() {
			T *ret = new T();			
			return ret;
		}
		
		void insert(cpShape * shape) {
			m_resFree.insert(shape);
		}

		void display();
		void update(cpFloat dt);

		bool remove_shape(cpShape *shape);

	public:
		shapes_map_t m_resFree;
		cpSpace *m_space;
	};


	template<typename T> struct CanBeBombPolicy { inline t_is_explosive CanBeBomb() {return boost::make_tuple(true, false);}};
	template<typename T> struct CantBeBombPolicy { inline t_is_explosive CanBeBomb() {return boost::make_tuple(false, false);}};
	template<typename T> struct TNTPolicy { inline t_is_explosive CanBeBomb() {return boost::make_tuple(true, true);}};

	template <
		template <typename> class InitializePolicy, 
		template <typename> class ActionPolicy = DefaultRenderPolicy,
		template <typename> class OtherPolicy0 = CantBeBombPolicy,
		template <typename> class OtherPolicy1 = NullPolicy
	>
	class TotemObj : public InitializePolicy<TotemObj<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> >, 
		public ActionPolicy<TotemObj<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> >,
		public OtherPolicy0<TotemObj<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> >,
		public OtherPolicy1<TotemObj<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> >
	{
	public:
		typedef InitializePolicy<TotemObj<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> > initialize_policy_t;
		typedef ActionPolicy<TotemObj<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> > action_policy_t;
		typedef OtherPolicy0<TotemObj<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> > other_policy0_t;
		typedef TotemObj<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> this_type;

		inline cpBody *GetBoby() {return m_pBoby;}
		inline cpShape *GetShape() {return m_pShape;}
		inline void SetActions() {
			m_actions.is_explosive = boost::bind(&other_policy0_t::CanBeBomb, this);
			m_actions.render_action = boost::bind(&TotemObj::render, this);
			m_actions._remove_from_space = boost::bind(&TotemObj::Remove, this, _1);
			m_actions._free = boost::bind(&TotemObj::free_self, this);
			m_actions._getObjInfo = boost::bind(&TotemObj::getObjInfo, this);
			m_pShape->data = &m_actions;
		}

		void Remove(cpSpace *space) {
			return initialize_policy_t::Remove(space);
		}
		//Chipmunk uses the Coulomb friction model, a value of 0.0 is frictionless
		inline void SetFrictionCoefficient(cpFloat u){ ASSERT(m_pShape); m_pShape->u = u;}

		//Elasticity of the shape. A value of 0.0 gives no bounce, while a value of 1.0 will give a  perfect bounce.
		inline void SetElasticity(cpFloat e){ ASSERT(m_pShape); m_pShape->e = e;}
		inline void SetFrictionAndElasticity(cpFloat u = 0.5, cpFloat e = 1.0) {
			SetFrictionCoefficient(u); SetElasticity(e);
		}

		static void free_self(TotemObj *pThis){
			delete pThis;
		}

		void render() {
			return action_policy_t::render();
		}
		const totem_obj_info_t *getObjInfo() {return m_objInfo;}

	public:
		cpBody *m_pBoby;
		cpShape *m_pShape;
		action_t m_actions;
		const totem_obj_info_t *m_objInfo;
	private:
		TotemObj() : m_pBoby(NULL), m_pShape(NULL) , m_objInfo(NULL){}
		friend class SpaceMgr;
	};

	template <typename T>
	class TotemObjRenderPolicy : public TextureRenderPolicy<T>{
	public:
		TotemObjRenderPolicy() : m_status(kTotemObjNormal) {}
		void render() {
			T* pT = static_cast<T*>(this);
			cpBody *body = pT->m_pBoby;
			const totem_obj_info_t *info = (const totem_obj_info_t *)pT->getObjInfo();
			if (kTotemObjNormal == m_status) {
				TextureRenderPolicy<T>::render();
			}
			else if (kTotemObjFallen == m_status) {
				if (NULL == m_fallen.get()) {
					m_fallen.reset(getAnimationFile<TotemObjFallen>(info));
				}		
				setVertexRot(m_fallen->getVerts(), m_fallen->getOriginalVerts(), body->p, body->rot);
				m_fallen->draw();
				if (m_fallen->runningOut()) {
					m_fallen->reset();
					setObjStatus(kTotemObjNormal);
				}
			}
			else if (kTotemObjBurn == m_status) {
				if (NULL == m_burnning.get()) {
					m_burnning.reset(getAnimationFile<TotemObjBurnning>(info));
					m_burnning->getDrawNormalFunction() = boost::bind(&TextureRenderPolicy<T>::render, this);
				}			
				setVertexRot(m_burnning->getVerts(), m_burnning->getOriginalVerts(), body->p, body->rot);				
				m_burnning->draw();
				if (m_burnning->runningOut()) {
					m_burnning->reset();
					setObjStatus(kTotemObjNormal);
				}
			}
			else if (kTotemObjOver == m_status) {
				if (NULL == m_broken.get()) {
					m_broken.reset(getAnimationFile<TotemObjBroken>(info));
				}
				setVertexRot(m_broken->getVerts(), m_broken->getOriginalVerts(), body->p, body->rot);
				//setVertexRot(m_broken->getVerts(), ((const totem_obj_info_t *)pT->getObjInfo())->verts, body->p, body->rot);
				m_broken->draw();
			}
			else if (kTotemObjPass == m_status) {
				if (NULL == m_pass.get()) {
					m_pass.reset(getAnimationFile<TotemObjPass>(info));
				}
				setVertexRot(m_pass->getVerts(), m_pass->getOriginalVerts(), body->p, body->rot);
				m_pass->draw();
			}
			else {
				TextureRenderPolicy<T>::render();
			}			
		}
		void setObjStatus(t_totem_obj_status status) {
			if (kTotemObjBurn == m_status) {
				if (kTotemObjNormal != status) return;
			}
			else if (kTotemObjOver == m_status) return;
			m_status = status;
		}
		bool &isCollision() {return m_isCollision;}
	public:
		t_totem_obj_status m_status;
		boost::scoped_ptr<TotemObjFallen> m_fallen;
		boost::scoped_ptr<TotemObjBurnning> m_burnning;
		boost::scoped_ptr<TotemObjBroken> m_broken;
		boost::scoped_ptr<TotemObjPass> m_pass;
		bool m_isCollision;
	};

	typedef TotemObj<PolygonShapeInitializePolicy, TextureRenderPolicy, CantBeBombPolicy> PolygonStaticObject;
	typedef TotemObj<PolygonShapeInitializePolicy, TextureRenderPolicy, CanBeBombPolicy> PolygonObject;
	typedef TotemObj<PolygonShapeInitializePolicy, TextureRenderPolicy, TNTPolicy> TNTObject;
	typedef TotemObj<StaticSegmentShapeInitializePolicy> StaticEdgeObj;
	typedef TotemObj<PolygonShapeInitializePolicy, TotemObjRenderPolicy, CantBeBombPolicy> TotemObject_t;

};

typedef totem_destroyer::PolygonStaticObject PolygonStaticObject;
typedef totem_destroyer::PolygonObject PolygonObject;
typedef totem_destroyer::TNTObject TNTObject;
typedef totem_destroyer::TotemObject_t TotemObject_t;

const cpVect G_MAIN_OBJ_EDGES[] ={ {8, 0}, {19, 0},  {27, 16}, 
{27, 39}, {0, 39}, {0, 16}};

#endif